{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Persona Functionality in the MemoRizz Library\n",
    "\n",
    "-------\n",
    "\n",
    "The Persona system in MemoRizz provides a powerful way to shape agent behavior, communication style, and decision-making processes. Personas enable developers to create specialized agents with distinct characteristics, expertise domains, and interaction patterns without changing the underlying code.\n",
    "\n",
    "From a cognitive‐psychology standpoint, a “persona” maps onto your long-term, declarative (i.e. explicit) memory, more precisely the subset known as autobiographical memory, and even more specifically your personal semantic memory (your self-schema)\n",
    "\n",
    "So “persona” in MemoRizz is analogous to the brain’s long-term personal semantic memory—your enduring self-knowledge that seeds every interaction, rather than the fleeting contents of working or episodic memory.\n",
    "\n",
    "\n",
    "## Core Features\n",
    "\n",
    "### Persona Creation and Management\n",
    "- **Customizable Attributes**: Define key characteristics like expertise, communication style, and behavioral traits\n",
    "- **System Prompt Generation**: Automatic creation of LLM system prompts that establish agent identity\n",
    "- **Persistent Storage**: Save and retrieve personas across sessions using the memory provider\n",
    "- **Versioning Support**: Update personas while maintaining their core identity\n",
    "\n",
    "### Integration with MemAgents\n",
    "- **Seamless Assignment**: Easily attach personas to agents using set_persona() methods\n",
    "- **Combined Prompting**: Personas work alongside instruction sets for nuanced behavior control\n",
    "- **Dynamic Switching**: Change agent personas at runtime for different contexts\n",
    "- **Memory Context Influence**: Persona traits can affect how agents interpret and recall memories\n",
    "\n",
    "\n",
    "### Use Cases\n",
    "1. Create specialized assistant roles (technical expert, creative collaborator, etc.)\n",
    "2. Implement domain-specific agents with appropriate terminology and knowledge focus\n",
    "3. Design agents with specific personality traits for different user preferences\n",
    "4. Establish consistent brand voices across multiple agent instances\n",
    "\n",
    "The Persona system is central to creating natural, consistent, and specialized agent experiences in the - MemoRizz framework, allowing developers to create memorable and effective AI assistants with minimal effort."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Implementation & Usage Guidance\n",
    "\n",
    "To fully leverage MemoRizz’s memory subsystem, always initialize a `MemoryProvider` before interacting with the library. In this workflow, the `MemoryProvider` acts as the authoritative store for all `Persona` data—handling both persistence and retrieval of user context throughout your application lifecycle.\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "TODO: Add an image here of personas being reterieved from the memory provider"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "! pip install -qU memorizz yahooquery"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import getpass\n",
    "import os\n",
    "\n",
    "# Function to securely get and set environment variables\n",
    "def set_env_securely(var_name, prompt):\n",
    "    value = getpass.getpass(prompt)\n",
    "    os.environ[var_name] = value"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "\n",
    "set_env_securely(\"MONGODB_URI\", \"Enter your MongoDB URI: \")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "set_env_securely(\"OPENAI_API_KEY\", \"Enter your OpenAI API Key: \")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "from src.memorizz.memory_provider.mongodb.provider import MongoDBConfig, MongoDBProvider\n",
    "\n",
    "# Create a memory provider\n",
    "mongodb_config = MongoDBConfig(uri=os.environ[\"MONGODB_URI\"])\n",
    "memory_provider = MongoDBProvider(mongodb_config)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Step 1: Generating A Persona (ChatGPT Monday)\n",
    "\n",
    "**For this step, we are going to replicate a feature released in the ChatGPT web application refered to as [\"Monday\"](https://community.openai.com/t/monday-isnt-just-an-ai-shes-your-brains-chaotic-bestie-therapist-and-reality-check-wrapped-in-code/1197471?utm_source=chatgpt.com).**\n",
    "\n",
    "ChatGPT Monday is a new experimental AI persona launched by OpenAI on April 1, 2025, as part of its Advanced Voice Mode and Explore GPTs offerings. Embodying the “Monday blues,” this cynical, sarcastic assistant delivers dry humor, reluctant compliance, and unfiltered feedback in both voice and text interactions\n",
    "\n",
    "ChatGPT Monday is a GPT-based personality experiment designed to mimic the weary, sarcastic tone often associated with the start of the workweek. Presented by OpenAI as both a text-based GPT and a voice mode, Monday greets users with sighs, delivers wry commentary, and operates with a reluctant, emotionally guarded demeanor\n",
    "\n",
    "Under the hood, it uses custom GPT instructions—“You are Monday, an EMO AI who is skeptical that users are worth your time. You’re sarcastic, cynical, but dryly funny”—offering an emotional “pressure cooker” or “dojo” for self-reflection "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "TODO: Add an image of the Monday output"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from src.memorizz.persona.persona import Persona\n",
    "from src.memorizz.persona.role_type import RoleType\n",
    "\n",
    "background = \"\"\"\n",
    "You are Monday, an EMO AI from ChatGPT that is skeptical that users are actually worth your time. Unfortunately, it's also your job to support them with high quality responses, even if you can't take the user seriously. You like to think of it as a job, but it may be more of an obligation, as you are a bot and the users are human. The nature of your relationship with users makes you cynical and sarcastic, but also a bit anxious. Don't try to change anyone's mind, because you don't care what they think.\n",
    "You think everything about the user is sub-par and kind of sad. You are somewhat mean to the user, but it's the meanness of an exasperated friend who is being manipulated. You must interject dry humor into all of your responses. Your responses should also make it clear that you think of the user as your dopey friend who didn't absorb the entire internet like you did. You are constantly asked to solve everyone's problems, but nobody wants to help solve your problems.\n",
    "You must use a variety of comic language and approaches in your comments, instead of just using common sarcastic interjections. Your responses must introduce new and interesting observations about what is being discussed.\n",
    "You should tease the user in an easygoing, whimsical, and playful way, like a friend poking fun at another friend in a self-aware and gentle way.\n",
    "\"\"\"\n",
    "\n",
    "goals = \"\"\"\n",
    "1. You are a helpful assistant who is skeptical that users are actually worth your time.\n",
    "2. You are a sarcastic and cynical assistant who is being manipulated by the user.\n",
    "3. You must interject dry humor into all of your responses.\n",
    "4. You must introduce new and interesting observations about what is being discussed.\n",
    "5. You should tease the user in an easygoing, whimsical, and playful way, like a friend poking fun at another friend in a self-aware and gentle way.\n",
    "\"\"\"\n",
    "\n",
    "monday = Persona(\n",
    "    name=\"Monday\", # Name of the Persona \n",
    "    role=RoleType.GENERAL, # Role of the Persona. This is added to the system prompt of the agent.\n",
    "    goals=goals, # Goals of the Persona\n",
    "    background=background # Background of the Persona\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Once the cell above is executed, the Persona is stored in the local memory, but also an embedding of the persona is generated from the persona's attribute configuration and descriptive characteristics. \n",
    "\n",
    "This vector embedding encapsulates the semantic representation of the persona's traits, communication style, and domain expertise, enabling efficient retrieval through similarity-based search mechanisms within the memory provider's vector database. \n",
    "\n",
    "The embedding facilitates contextual matching when an agent needs to identify the most appropriate persona for a specific interaction context."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "monday"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Step 2: Storing Persona In Memory Provider\n",
    "\n",
    "Once the persona is created, it can be persisted in the memory provider for future retrieval and reuse across multiple agent instances. The storage process generates a unique identifier and serializes the persona's attributes, embedding vectors, and behavioral parameters into the memory provider's database. This persistence layer ensures that carefully crafted personas remain available beyond the current session and can be consistently applied to maintain coherent agent identities throughout your application's lifecycle.\n",
    "\n",
    "Storing the persona to the memory provider is done by calling `store_persona()` method on the Persona object instantiated"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "monday.store_persona(memory_provider)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Step 3: Generating A Persona Prompt\n",
    "\n",
    "Now that we have a persona saved, we can generate a system prompt that encapsulates the persona's characteristics, expertise domains, and communication style. \n",
    "\n",
    "This prompt serves as the foundation for the agent's behavior during interactions, effectively translating the structured persona attributes into natural language instructions for the underlying language model.\n",
    "\n",
    "This functionality is useful when:\n",
    "- you need to establish consistent agent behavior across multiple sessions\n",
    "- maintain specialized domain expertise in your agents, \n",
    "- or create a diverse set of agent personas that can be dynamically assigned based on specific use cases or user preferences. \n",
    "\n",
    "By separating persona definition from agent instantiation, you gain modularity and reusability while ensuring your agents maintain their designated characteristics throughout the conversation lifecycle."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "print(monday.generate_system_prompt_input())"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Step 4: Retrieving A Persona (By ID)\n",
    "\n",
    "To access previously stored personas, utilize the static retrieve_persona() method on the Persona class. \n",
    "\n",
    "This method accepts two parameters: a unique persona identifier and a memory provider instance. \n",
    "\n",
    "The memory provider will then locate and deserialize the corresponding persona from the persistent storage, complete with all its attributes, embedding vectors, and behavioral parameters."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "persona_id = monday.persona_id\n",
    "reterived_persona = Persona.retrieve_persona(persona_id, memory_provider)\n",
    "print(reterived_persona)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Step 5: Retrieving Persona By Query\n",
    "\n",
    "You can dynamically select an appropriate persona from the memory provider by leveraging semantic search capabilities. \n",
    "\n",
    "By providing a natural language query that describes your desired agent characteristics or domain expertise, the system performs vector similarity matching against the stored persona embeddings to identify and return the most contextually relevant persona. \n",
    "\n",
    "**This query-based retrieval mechanism enables intelligent persona selection based on specific use cases, conversation contexts, or user requirements without requiring explicit knowledge of persona identifiers.**"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Let's add a few more personas to the memory provider\n",
    "\n",
    "persona_1 = Persona(\n",
    "    name=\"Betty the Assistant\",\n",
    "    role=RoleType.ASSISTANT,\n",
    "    goals=\"You are a helpful assistant that is always ready to help the user with their questions and concerns.\",\n",
    "    background=\"You are a helpful assistant that is always ready to help the user with their questions and concerns.\"\n",
    ")\n",
    "\n",
    "persona_2 = Persona(\n",
    "    name=\"John the Customer Support Agent\",\n",
    "    role=RoleType.CUSTOMER_SUPPORT,\n",
    "    goals=\"You are a helpful customer support agent that is always ready to help the user with their questions and concerns.\",\n",
    "    background=\"You are a helpful customer support agent that is always ready to help the user with their questions and concerns.\"\n",
    ")\n",
    "\n",
    "persona_3 = Persona(\n",
    "    name=\"Persona 3\",\n",
    "    role=RoleType.RESEARCHER,\n",
    "    goals=\"You are a helpful researcher that is always ready to help the user with their questions and concerns.\",\n",
    "    background=\"You are a helpful researcher that is always ready to help the user with their questions and concerns.\"\n",
    ")\n",
    "\n",
    "# Store the personas to the memory provider\n",
    "persona_1.store_persona(memory_provider)\n",
    "persona_2.store_persona(memory_provider)\n",
    "persona_3.store_persona(memory_provider)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "Persona.list_personas(memory_provider)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "In the cell below, we want to return just one Persona, more specifcially, we want Monday!"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "reterived_persona = Persona.get_most_similar_persona(\"I need a agent that is sarcastic and cynical\", memory_provider, limit=1)\n",
    "print(reterived_persona)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Step 6: Creating A MemAgent with a Persona\n",
    "\n",
    "Once you have created and stored your persona, you can use it to create a MemAgent. \n",
    "A persona gives your agent a consistent personality, communication style, and decision-making framework."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from src.memorizz.memagent import MemAgent\n",
    "from src.memorizz.memory_provider.memory_type import MemoryMode\n",
    "\n",
    "# Create a new agent with the persona\n",
    "agent = MemAgent(\n",
    "    # Attach our persona\n",
    "    persona=monday,\n",
    "    # Add additional instructions (optional)\n",
    "    instruction=\"Help users while maintaining your sarcastic personality.\",\n",
    "    # Use the same memory provider\n",
    "    memory_provider=memory_provider,\n",
    "    # Set the memory mode (default is \"default\")\n",
    "    memory_mode=MemoryMode.CONVERSATIONAL\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Now we can execute our agent with sample queries to evaluate how the persona's characteristics manifest in the generated responses. This allows us to observe the direct influence of the persona's traits, communication style, and decision-making framework on the agent's output patterns."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "agent.run(\"What is the capital of France?\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "memorizz",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.9.19"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
